﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using NetBay.Core.Silverlight.ComponentModel;
using Rio.Framework.WCFService.RioWcfServices;
using System.Threading;

namespace Rio.Framework.WCFService
{
    public class FileSlice : IDisposable
    {
        #region Attributes

        /// <summary>
        /// Boost for Timeout to send one Slice (milliseconds)
        /// </summary>
        private uint m_uiBoostTimeoutSendSlice = 5 * ServiceManager.UploadThreadTimeout;

        /// <summary>
        /// Objet d'attente de l'exécution de la tâche
        /// </summary>
        private ManualResetEvent m_oSynchro;

        internal ManualResetEvent Finished = new ManualResetEvent(false);

        public event EventHandler Finalized;
        
        /// <summary>
        /// Retry count to send one Slice if send fail
        /// </summary>
        private int m_iRetryToSendSlice = ServiceManager.UploadThreadAttempt;

        /// <summary>
        /// Timeout to send one Slice (milliseconds)
        /// </summary>
        private uint m_uiTimeoutSendSlice = ServiceManager.UploadThreadTimeout;

        private PostDocument m_oPostDocument;

        int m_iSliceId;
        bool m_bFinalSlice;
        Byte[] m_oSliceToSend;
        string m_sDestinationRelativePath;
        string m_sStoragePath;
        bool m_bOverwrite;
        
        #endregion

        #region Accessors
        public string ErrorMessage { get; private set; }
        /// <summary>
        /// Timeout to send one Slice
        /// </summary>
        public uint TimeoutSendSlice
        {
            get { return m_uiTimeoutSendSlice; }
        }
        /// <summary>
        /// Boost for Timeout to send one Slice
        /// </summary>
        public uint BoostTimeoutSendSlice
        {
            get { return m_uiBoostTimeoutSendSlice; }
        }
        /// <summary>
        /// Retry count to send one Slice if send fail
        /// </summary>
        public int RetryToSendSlice
        {
            get { return m_iRetryToSendSlice; }
        }
        #endregion

        #region functions

        /// <summary>
        /// Return the internal ManualResetEvent
        /// </summary>
        /// <returns></returns>
        public ManualResetEvent Notify()
        {
            return Finished;
        }

        /// <summary>
        /// Set the internal ManualResetEvent
        /// </summary>
        public void End()
        {
            Finished.Set();
        }

        /// <summary>
        /// Define all low parameters for this class
        /// </summary>
        /// <param name="p_uiTimeoutSendSlice">Timeout to send one Slice (seconds)</param>
        /// <param name="p_uiBoostTimeoutSendSlice"></param>
        /// <param name="p_iRetryToSendSlice">Retry count to send one Slice if send fail</param>
        public void DefineLowParameters(uint p_uiTimeoutSendSlice, uint p_uiBoostTimeoutSendSlice, int p_iRetryToSendSlice)
        {
            m_uiTimeoutSendSlice = p_uiTimeoutSendSlice;
            m_uiBoostTimeoutSendSlice = p_uiBoostTimeoutSendSlice;
            m_iRetryToSendSlice = p_iRetryToSendSlice;
        }

        
        /// <summary>
        /// Post one slice to server
        /// </summary>
        /// <param name="p_iSliceId">Slice Id</param>
        /// <param name="p_bFinalSlice"></param>
        /// <param name="p_oSliceToSend">Slice to send</param>
        public void Prepare(PostDocument p_oPostDocument, int p_iSliceId, bool p_bFinalSlice, Byte[] p_oSliceToSend, string p_sDestinationRelativePath = "", string p_sStoragePath = "", bool p_bOverwrite = false)
        {
            //_doneEvent = doneEvent;
            m_oPostDocument = p_oPostDocument;
            m_iSliceId = p_iSliceId;
            m_bFinalSlice = p_bFinalSlice;
            m_oSliceToSend = p_oSliceToSend;
            m_sDestinationRelativePath = p_sDestinationRelativePath;
            m_sStoragePath = p_sStoragePath;
            m_bOverwrite = p_bOverwrite;
        }


        /// <summary>
        ///  Wrapper method for use with thread pool.
        /// </summary>
        /// <param name="threadContext"></param>
        public void ThreadPoolCallback(Object threadContext)
        {
            bool l_bRet;
            int l_iRetry = m_iRetryToSendSlice;
            do
            {
                l_bRet = PostOneSliceAttempt(m_iSliceId, m_bFinalSlice, m_oSliceToSend, m_sDestinationRelativePath, m_sStoragePath);
                if (l_bRet == true)
                    break;
            } while (--l_iRetry > 0);
            
            // Check if ok
            if (l_bRet)
            {
                Finished.Set();
            }
            if (Finalized != null) 
                Finalized(this, null);
        }
        
        /// <summary>
        /// Attempt to send a slice to the server
        /// </summary>
        /// <param name="p_iSliceId">Id of the slice (its index)</param>
        /// <param name="p_bFinalSlice">Is the finalslice</param>
        /// <param name="p_oSliceToSend">Byte array to send</param>
        /// <returns></returns>
        private bool PostOneSliceAttempt(int p_iSliceId, bool p_bFinalSlice, byte[] p_oSliceToSend, string p_sDestinationRelativePath = "", string p_sStoragePath = "", bool p_bOverwrite = false)
        {
            BackgroundWorkerTimeout bwt;
            bool l_bRet;
            try
            {
                // Use the boost timeout if its the final slice
                uint l_uiTimeout = m_uiTimeoutSendSlice;
                if (p_bFinalSlice == true)
                    l_uiTimeout = BoostTimeoutSendSlice;
                bwt = new BackgroundWorkerTimeout(l_uiTimeout);
                bwt.DoWork += Execute;

                // Création des arguments
                ArgsForExecute l_oArgs = new ArgsForExecute(m_oPostDocument, p_iSliceId, p_bFinalSlice, p_oSliceToSend, p_sDestinationRelativePath, p_sStoragePath, p_bOverwrite);
                 
                // Exécution de la requete
                bwt.RunWorkerAsync(l_oArgs);

                // All Ok
                l_bRet = true;
            }
            catch (Exception ex) /*(TimeoutException)*/
            {
                ErrorMessage = "PostOneSliceAttempt() return " + ex.ToString();
                l_bRet = false;
            }
            return l_bRet;
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Execute(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            // Retreive the BackgroundWorkerTimeout
            BackgroundWorkerTimeout l_oBackgroundWorkerTimeout = (BackgroundWorkerTimeout)sender;
            // Retreive parameters
            ArgsForExecute l_oArgs = (ArgsForExecute)e.Argument;

            try
            {
                // Prepare the PostDocument job
                WCFServiceClient l_oRioWCFServices = ServiceManager.GetRioWCFServiceClient();

                // Dispatch postdocument action
                if (!string.IsNullOrEmpty(m_sDestinationRelativePath))
                {
                    l_oRioWCFServices.PostDocumentWithSegmentAndDirectoryMoveCompleted += l_oRioWCFServices_PostDocumentWithSegmentCompleted;
                    // Prepare the synchonisation of completed PostDocument
                    // To wait the Completed method
                    m_oSynchro = new ManualResetEvent(false);
                    // Launch the request of 'PostDocument'
                    l_oRioWCFServices.PostDocumentWithSegmentAndDirectoryMoveAsync(l_oArgs.SliceToSend, m_sDestinationRelativePath, m_sStoragePath, l_oArgs.This.Guid, l_oArgs.SliceId, l_oArgs.FinalSlice, m_bOverwrite, e);
                }
                else
                {
                    l_oRioWCFServices.PostDocumentWithSegmentCompleted += l_oRioWCFServices_PostDocumentWithSegmentCompleted;
                    // Prepare the synchonisation of completed PostDocument
                    // To wait the Completed method
                    m_oSynchro = new ManualResetEvent(false);

                    // Launch the request of 'PostDocument'
                    string l_oFileName = l_oArgs.This.FileInfo.Name;
                    if (l_oFileName.Length > ServiceManager.FileMaxLength)
                    {
                        l_oFileName = System.IO.Path.GetFileNameWithoutExtension(l_oArgs.This.FileInfo.Name).Substring(0, ServiceManager.FileMaxLength) + System.IO.Path.GetExtension(l_oArgs.This.FileInfo.Name);
                    }
                    l_oRioWCFServices.PostDocumentWithSegmentAsync(l_oArgs.SliceToSend, l_oFileName, l_oArgs.This.UserId, l_oArgs.This.Guid, l_oArgs.SliceId, l_oArgs.FinalSlice, e);
                }

               
                // Wait if any the completed method
                int wait = WaitHandle.WaitAny(new WaitHandle[] { m_oSynchro }, (int)l_oBackgroundWorkerTimeout.TimeOut);
                if (wait == WaitHandle.WaitTimeout)
                {
                    throw new TimeoutException("Timeout(" + l_oBackgroundWorkerTimeout.TimeOut / 1000 + ")s to execute(" + ")");
                }
                // Check for any error in the completed method
                if (e.Result != null)
                    throw (Exception)e.Result;
                e.Result = true;
            }
            catch (Exception ex)
            {
                e.Result = ex;
            }
        }

        private void l_oRioWCFServices_PostDocumentWithSegmentCompleted(object sender, PostDocumentWithSegmentCompletedEventArgs e)
        {
            // Retreive the DoWorkEventArgs
            System.ComponentModel.DoWorkEventArgs l_DoWorkEventArgs =
                (System.ComponentModel.DoWorkEventArgs)e.UserState;
            if (e.Error != null)
            {
                l_DoWorkEventArgs.Result = e.Error;
            }
            else
            {
                if (e.Result.IsValid == false)
                {
                    l_DoWorkEventArgs.Result = new Exception(e.Result.FullErrorMessage);

                }
                else
                {
                    m_oPostDocument.m_oThumnail = e.Result;
                }
            }
            m_oSynchro.Set();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void l_oRioWCFServices_PostDocumentWithSegmentCompleted(object sender, PostDocumentWithSegmentAndDirectoryMoveCompletedEventArgs e)
        {
            // Retreive the DoWorkEventArgs
            System.ComponentModel.DoWorkEventArgs l_DoWorkEventArgs =
                (System.ComponentModel.DoWorkEventArgs)e.UserState;
            if (e.Error != null)
            {
                l_DoWorkEventArgs.Result = e.Error;
            }
            else
            {
                if (e.Result.IsValid == false)
                {
                    l_DoWorkEventArgs.Result = new Exception(e.Result.FullErrorMessage);

                }
                else
                {
                    m_oPostDocument.m_oThumnail = e.Result;
                }
            }
            m_oSynchro.Set();
        }

        /// <summary>
        /// Dispose objects
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Dispose managed and unmanaged object following the disposing attribute
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
            }
            m_oSliceToSend = null;
        }

        #endregion

        #region Private classes
        private class ArgsForExecute
        {
            /// <summary>
            /// 
            /// </summary>
            private PostDocument m_oThis;
            /// <summary>
            /// 
            /// </summary>
            private int m_iSliceId;
            /// <summary>
            /// 
            /// </summary>
            private bool m_bFinalSlice;
            /// <summary>
            /// 
            /// </summary>
            private byte[] m_aSliceToSend;
            /// <summary>
            /// Path 
            /// </summary>
            private string m_sDestinationRelativePath;
            /// <summary>
            /// Path 
            /// </summary>
            private string m_sStoragePath;
            /// <summary>
            /// 
            /// </summary>
            private bool m_bOverwrite;
            /// <summary>
            /// 
            /// </summary>
            public int SliceId
            {
                get { return m_iSliceId; }
            }
            /// <summary>
            /// 
            /// </summary>
            public byte[] SliceToSend
            {
                get { return m_aSliceToSend; }
            }
            /// <summary>
            /// Destination full path on the server
            /// </summary>
            public string DestinationRelativePath
            {
                get { return m_sDestinationRelativePath; }
            }
            /// <summary>
            /// Destination full path on the server
            /// </summary>
            public string StoragePath
            {
                get { return m_sStoragePath; }
            }
            /// <summary>
            /// 
            /// </summary>
            public PostDocument This
            {
                get { return m_oThis; }
            }

            /// <summary>
            /// 
            /// </summary>
            public bool FinalSlice
            {
                get { return m_bFinalSlice; }
            }

            public bool p_bOverwrite
            {
                get { return m_bOverwrite; }
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="p_oThis"></param>
            /// <param name="p_iSliceId"></param>
            /// <param name="p_bFinalSlice"></param>
            /// <param name="p_oSliceToSend"></param>
            public ArgsForExecute(PostDocument p_oThis, int p_iSliceId, bool p_bFinalSlice, byte[] p_oSliceToSend, string p_sDestinationRelativePath = "", string StoragePath = "", bool p_bOverwrite = false)
            {
                m_oThis = p_oThis;
                m_bFinalSlice = p_bFinalSlice;
                m_iSliceId = p_iSliceId;
                m_aSliceToSend = p_oSliceToSend;
                m_sDestinationRelativePath = p_sDestinationRelativePath;
                m_sStoragePath = StoragePath;
                m_bOverwrite = p_bOverwrite;
            }

         }
        #endregion
    }
}
